import processing.core.*; 
import processing.xml.*; 

import controlP5.*; 
import processing.opengl.*; 

import java.applet.*; 
import java.awt.Dimension; 
import java.awt.Frame; 
import java.awt.event.MouseEvent; 
import java.awt.event.KeyEvent; 
import java.awt.event.FocusEvent; 
import java.awt.Image; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class albero extends PApplet {

 


int maxw = (int)(screen.width / 1.4f);
int maxh = (int)(screen.height / 1.4f);

int initialPoints = 3;
int numLeaves = 2;
public int maxIterations = 5;
boolean enable3d = true;
boolean enable2d = true;
boolean enableyzAxis = false;
boolean enableAnimation = true;

float start_angleXY = -PI/2;
float start_angleXZ = -PI/8;
float incAngle = QUARTER_PI; //45\u00b0
int raggio  = 80;
float decr_raggio = 1.4f;
float rotateAlfa = 0;
float rotateInc = 0.0f;
float rotateGlobal = 0;
float scaleTree = 1;
int transTreeY = 0;
int transTreeX = 0;
boolean bStopRotation = false;

float rotationX = 1;
float rotationY = 1;
float rotationZ = 1;

int maxPoints = 10000;
float inc;
float raggio_temp = raggio;


ControlP5 controlP5;
ControlWindow controlWindow;
int contrWinHeight = 500;
int contrWinWidth = 250;

PGraphics pg;

class treePoint
{
  public float x;
  public float y;
  public float z;
  float angle;
  float angleXZ;
  boolean noredraw;
  treePoint parent = null;
  public int level = 0;

  treePoint(float ax,float ay,float az,float aangle,float start_angleXZ,treePoint aparent)
  {
    x = ax;
    y=ay;
    z = az;
    angle=(aangle);
    angleXZ = start_angleXZ;
    noredraw = false;
    parent = aparent;
    if (parent!=null)
      level = parent.level+1;
    //println("added x:" + x + " y:" + y + " angle:" + angle);

    if (frameCount%10==0)
      println("level:" + level + " maxIterations:" + maxIterations);
  }
}

ArrayList points;

public void setAmbient()
{
  raggio_temp = raggio;
  rotateAlfa = 0;

  points = new ArrayList();

  maxPoints = 0;
  for (int i=0;i<maxIterations;i++)
    maxPoints += initialPoints * (int)pow(2,i);

  inc = 2*PI/initialPoints;

  treePoint basePoint = new treePoint(0,0,0,-PI/2,0,null);
  treePoint zeroPoint = new treePoint(0,0,0,-PI/2,0,basePoint);
  basePoint.noredraw=true;
  zeroPoint.noredraw=true;
  points.add(basePoint);
  points.add(zeroPoint);

  for (int j=0;j<initialPoints;j++)
  {
    float x1 = cos(start_angleXY)*raggio;
    float y1 = sin(start_angleXY)*raggio;
    float z1 = sin(start_angleXZ) * raggio;
    points.add(new treePoint(0+x1,0+y1,0+z1,start_angleXY,start_angleXZ,zeroPoint));
    start_angleXY+=inc;
  }
}


public void setup() {
  //translate(0,0);
  size(maxw,maxh,OPENGL);
  //size(screen.width, screen.height);
  //pg = createGraphics(maxw, maxh, P3D);
  //pg.background(0, 0, 0);

  setAmbient();
  smooth(); 

  createUi();
}

public void createPoints()
{
  int psize = points.size();
  float x1,y1,z1,x2,y2,z2;
  
  for (int j=0;j<psize;j++)
  {
    if (points.size()<=j)
      return;

    treePoint p = (treePoint)points.get(j);
    
    if (p==null)
      return;
    
    if (p.noredraw==false)
    {
     float leafAngle = 2*incAngle/numLeaves;
      
     for (int k=0;k<numLeaves;k++)
     {
       x1 = raggio_temp*cos((p.angle) + incAngle - 2*k*leafAngle + rotateAlfa) + p.x;
       y1 = raggio_temp*sin((p.angle) + incAngle - 2*k*leafAngle + rotateAlfa) + p.y;
       z1 = raggio_temp*sin((p.angleXZ)) + p.z;

       points.add(new treePoint(x1,y1,z1,(p.angle) + incAngle - 2*k*leafAngle,p.angleXZ,p));
     }
      
     /*x1 = raggio_temp*cos((p.angle)+incAngle + rotateAlfa) + p.x;
     y1 = raggio_temp*sin((p.angle)+incAngle + rotateAlfa) + p.y;
     z1 = raggio_temp*sin((p.angleXZ)) + p.z;
    
     x2 = raggio_temp*cos((p.angle)-incAngle + rotateAlfa) + p.x;
     y2 = raggio_temp*sin((p.angle)-incAngle + rotateAlfa) + p.y;
     z2 = raggio_temp*sin((p.angleXZ)) + p.z;

      points.add(new treePoint(x1,y1,z1,(p.angle)+incAngle,p.angleXZ,p));
      points.add(new treePoint(x2,y2,z2,(p.angle)-incAngle,p.angleXZ,p));
      */

      p.noredraw=true;
    }
  }

  raggio_temp/=decr_raggio;
}

public void draw()
{
  controlP5.draw();

  if (enableAnimation)
  {
    if (enableAnimation /*&& frameCount % 5 == 0*/)
    {
          incAngle *= 1.001f;
          setAmbient();
    }
  }
  
  while (points.size()<maxPoints)
      createPoints();

  redrawTree();
}

public void redrawTree()
{
  int psize = points.size();

  background(20);
  lights();
  smooth();
  noStroke();

  translate(width/2+transTreeX,-transTreeY+20+height/2-raggio/2);
  setStroke(1);

  if (frameCount%100==0)
  {
    println("points:" + initialPoints + " incAngle:" + incAngle + " decr_raggio:" + decr_raggio);
    println("psize is:" + psize);
    println("max iteration is:" + maxIterations);
  }

  rotateAlfa+=rotateInc;

  //rotateY(PI/4);
  rotateX(rotationX);
  rotateY(rotationY);
  rotateZ(rotationZ);
  
  if (bStopRotation)
    rotateX(rotateGlobal);
  else 
    rotateX(rotateGlobal+=0.01f);
  
  scale(scaleTree);

  if (enableyzAxis)
  {
    PFont font = createFont("Arial",18,true); 
    textFont(font);
    
    stroke(255,0,0);
    line(0,0,0,200,0,0);
    text("X", 210, 0, 0); 
    
    stroke(0,255,0);
    line(0,0,0,0,200,0);
    text("Y", 0, 210, 0);    
    
    stroke(0,0,255);
    line(0,0,0,0,0,200);
    text("Z", 0, 0, 210);    
  }
  
  directionalLight(200,200,0,20,10,10);
  //directionalLight(255,0,0,0,0,10);
  
  if (psize==0)
    return;
    
  for (int j=0;j<psize;j++)
  {
    //println("getting element " + j);
    
    if (points.size()<=j)
      return;
    
    treePoint p = (treePoint)points.get(j);

    if (p.parent!=null)
    {
      if (enable2d)
      {
        setStroke(p);
        line(p.x,p.y,p.parent.x,p.parent.y);
      }
      
      if (enable3d)
      {
        noStroke();
        pushMatrix();
  
        float delta = dist(p.x,p.y,p.parent.x,p.parent.y);
        translate(p.parent.x,p.parent.y,p.parent.z);
        box(delta/5);
        popMatrix();
        
        pushMatrix();
        //translate(p.x,p.y);
        translate((p.parent.x+p.x)/2,(p.parent.y+p.y)/2,(p.parent.z+p.z)/2);
        
        fill(getColorFromPoint(p));
        //fill(p.level*30,255-p.level*30,p.level*20);
        
        rotate(p.angle);
        rotateY(-p.angleXZ);
        
        box(delta,delta/5,delta/5);
        popMatrix();
      }
      
     /*
      beginShape();
 
      vertex(p.x,p.y,0);
      vertex(p.x-delta,p.y,0);
      vertex(p.parent.x,p.parent.y,0);
      vertex(p.parent.x-delta,p.parent.y,0);
      endShape(CLOSE);
      
      beginShape();
      vertex(p.x,p.y,0);
      vertex(p.x,p.y,2);
      vertex(p.parent.x,p.parent.y,0);
      vertex(p.parent.x,p.parent.y,2);
       endShape(CLOSE);
      beginShape();

      vertex(p.x,p.y,2);
      vertex(p.x-delta,p.y,2);
      vertex(p.parent.x,p.parent.y,2);
      vertex(p.parent.x-delta,p.parent.y,2);
      endShape(CLOSE);
      beginShape();
 
      vertex(p.x-delta,p.y,0);
      vertex(p.x-delta,p.y,2);
      vertex(p.parent.x-delta,p.parent.y,0);
      vertex(p.parent.x-delta,p.parent.y,2);
      endShape(CLOSE);*/

    }
  }
}

public void setStroke(int level)
{
  //randomSeed(frameCount);
  float maxIteration = maxIterations;
  float cred = maxIteration*10;
  float cgreen = maxIteration*5;
  float cblue = maxIteration*5;
  //println("red:" + cred);
  stroke(map(level,0,maxPoints,0,cred),map(level,0,maxPoints,0,cgreen),map(level,0,maxPoints,0,cblue),map(level,0,maxPoints,200,255));
  strokeWeight(map(level,0,maxPoints,3,1));
} 

public void setStroke(treePoint p)
{
  stroke(getColorFromPoint(p));
  strokeWeight(map(maxIterations+1-p.level,1,10,1,5));
} 

public int getColorFromPoint(treePoint p)
{
  float maxIteration = maxIterations+1;
  float cred = p.level*20;
  float cgreen = p.level*10;
  float cblue = p.level*5;
  
  float ired = map(p.level,1,maxIteration,255,cred) + (int)(100*sin(radians(frameCount)));
  float igreen = map(p.level,1,maxIteration,255,cgreen) + (int)(100*cos(radians(frameCount)));
  float iblue = map(p.level,1,maxIteration,255,cblue) - (int)(100*sin(radians(frameCount)));
  float itrasp = map(p.level,0,maxIteration,200,255);
  int c = color(ired,igreen,iblue,itrasp);
  return c;
}



public void keyReleased() 
{
  boolean found = false;  

  if (key == 'q' || key == 'Q') 
  {      
    initialPoints++;
    found=true;
  }
  else if (key == 'W' || key == 'w') 
  {  
    initialPoints--;
    found=true;
  }
  else if (key == 'E' || key == 'e') {
    decr_raggio+=0.1f;
    found=true;
  }
  else if (key == 'R' || key == 'r') {
    decr_raggio-=0.1f;
    found=true;
  }
  else if (key == 'T' || key == 't') {
    incAngle+=PI/180;
    found=true;
  }
  else if (key == 'Y' || key == 'y') {
    incAngle-=PI/180;
    found=true;
  }
  else if (key == 'p' || key == 'P') 
  {
  }

  if (found)
  {
    setAmbient();
    draw();
  }
}



public void createUi()
{
  controlP5 = new ControlP5(this);
  ControlFont cfont;

  //String[] fontList = PFont.list();
  //println(fontList);
  PFont pfont;

  char[] charset = new char[4];
  for (int i = 0; i < 4; i++) 
  {
    charset[i] = Character.toChars(0x2190 + i)[0];
  }

  pfont = createFont("Arial",8,true,charset); 
  textFont(pfont,10); 
  cfont = new ControlFont(pfont);
  
  controlWindow = controlP5.addControlWindow("controlP5window",maxw+100,100,contrWinWidth,contrWinHeight);
  controlWindow.hideCoordinates();
  controlWindow.setBackground(color(40));      
  
  int hVideoIncrement = 0;

  Button btnStopRotate = controlP5.addButton("stop/start rotation",0,10,hVideoIncrement+=20,100,14);
  btnStopRotate.setId(13);
  btnStopRotate.setWindow(controlWindow);

  Button btnRotateX = controlP5.addButton("rotate X",0,10,hVideoIncrement+=20,50,14);
  btnRotateX.setId(14);
  btnRotateX.setWindow(controlWindow);
  Button btnRotateY = controlP5.addButton("rotate Y",0,10,hVideoIncrement+=20,50,14);
  btnRotateY.setId(15);
  btnRotateY.setWindow(controlWindow);
  Button btnRotateZ = controlP5.addButton("rotate Z",0,10,hVideoIncrement+=20,50,14);
  btnRotateZ.setId(16);
  btnRotateZ.setWindow(controlWindow);

  Button btnUp = controlP5.addButton("U",1.1f,30,hVideoIncrement+=20,15,14);
  btnUp.setId(9);
  btnUp.setWindow(controlWindow);
  Button btnDown = controlP5.addButton("D",1.1f,30,hVideoIncrement+=20,15,14);
  btnDown.setId(10);
  btnDown.setWindow(controlWindow);
  Button btnRight = controlP5.addButton("R",1.1f,50,hVideoIncrement-20,15,14);
  btnRight.setId(11);
  btnRight.setWindow(controlWindow);
  Button btnLeft = controlP5.addButton("L",1.1f,10,hVideoIncrement-20,15,14);
  btnLeft.setId(12);
  btnLeft.setWindow(controlWindow);

  Button btnPlus = controlP5.addButton("Zoom +",1.1f,10,hVideoIncrement+=20,50,14);
  btnPlus.setId(7);
  btnPlus.setWindow(controlWindow);
  
  Button btnMinus = controlP5.addButton("Zoom -",1.1f,70,hVideoIncrement,50,14);
  btnMinus.setId(8);
  btnMinus.setWindow(controlWindow);

  Slider sRotateInc = controlP5.addSlider("sRotateInc",0,PI,rotateInc,10,hVideoIncrement+=20,100,14);
  sRotateInc.setId(6);
  sRotateInc.setLabel("Branch rotation");
  sRotateInc.setWindow(controlWindow);

  Slider sIterations = controlP5.addSlider("maxIterations",0,20,maxIterations,10,hVideoIncrement+=20,100,14);
  sIterations.setId(5);
  sIterations.setLabel("Max iterations");
  sIterations.setWindow(controlWindow);

  Slider iNumRami = controlP5.addSlider("InitialBranches",1,50,initialPoints,10,hVideoIncrement+=20,100,14);
  iNumRami.setId(1);
  iNumRami.setLabel("Initial Branches");
  iNumRami.setWindow(controlWindow);

  Slider iNumLeaves = controlP5.addSlider("LeavesNumber",1,50,numLeaves,10,hVideoIncrement+=20,100,14);
  iNumLeaves.setId(21);
  iNumLeaves.setLabel("Leaves number");
  iNumLeaves.setWindow(controlWindow);

  Slider incRaggio = controlP5.addSlider("RadiusLength",1,500,raggio,10,hVideoIncrement+=20,100,14);
  incRaggio.setId(2);
  incRaggio.setLabel("Radius length");
  incRaggio.setWindow(controlWindow);

  Slider sAngle = controlP5.addSlider("BranchAngle",1,360,incAngle,10,hVideoIncrement+=20,180,14);
  sAngle.setId(3);
  sAngle.setLabel("Branch Angle");
  sAngle.setWindow(controlWindow);

  Slider sIncRaggio = controlP5.addSlider("incAngle",0,5,decr_raggio,10,hVideoIncrement+=20,180,14);
  sIncRaggio.setId(4);
  sIncRaggio.setLabel("Radius Increment");
  sIncRaggio.setWindow(controlWindow);
  
  Toggle enable2d = controlP5.addToggle("Enable 2d",true,10,hVideoIncrement+=30,10,10);
  enable2d.setId(18);
  enable2d.setWindow(controlWindow);
  
  Toggle enable3d = controlP5.addToggle("Enable 3d",true,10,hVideoIncrement+=30,10,10);
  enable3d.setId(17);
  enable3d.setWindow(controlWindow);
  
  Toggle enablexyz = controlP5.addToggle("Show axis",false,10,hVideoIncrement+=30,10,10);
  enablexyz.setId(19);
  enablexyz.setWindow(controlWindow);

  Toggle enableAnimation = controlP5.addToggle("Enable animation",true,10,hVideoIncrement+=30,10,10);
  enableAnimation.setId(20);
  enableAnimation.setWindow(controlWindow);
  
  //controlP5.addToggle("toggleValue",true,100,240,100,20).setMode(ControlP5.SWITCH);  
  
  //Knob myKnobA = controlP5.addKnob("knob",0,2*PI,0,0,100,50);

  controlP5.controller("InitialBranches").setMin(1);  
  controlP5.controller("RadiusLength").setMin(1);  
  controlP5.controller("incAngle").setMin(0);
  
 
}



public void controlEvent(ControlEvent theEvent) {
  println("entered control event");
  switch(theEvent.controller().id()) {
    case(1): // 
      initialPoints = (int)(theEvent.controller().value());
    break;
    case(2):  // 
    raggio = (int)(theEvent.controller().value());
    break;  
    case(3):  // 
    incAngle = radians((int)(theEvent.controller().value()));
    break;  
    case(4):  // 
    decr_raggio = (float)(theEvent.controller().value());
    break;  
    case(5):  // 
    maxIterations = (int)(theEvent.controller().value());
    break;     
    case(6):  // 
    rotateInc = (float)(theEvent.controller().value());
    break;  
    case(7):  // 
    scaleTree*=1.1f;
    break;  
    case(8):  // 
    scaleTree/=1.1f;
    break;  
    case(9):  // 
    transTreeY+=10;
    break;  
    case(10):  // 
    transTreeY-=10;
    break;  
    case(11):  // 
    transTreeX+=10;
    break;  
    case(12):  // 
    transTreeX-=10;
    break;  
    case(13):  // 
    bStopRotation = !bStopRotation;
    break;
     case(14):  // 
    rotationX *= 1.1f;
    break;
     case(15):  // 
    rotationY *= 1.1f;    
    break;
     case(16):  // 
    rotationZ *= 1.1f;    
    break;
    case(17):  // 
    enable3d = !enable3d;
    break;
    case(18):  // 
    enable2d = !enable2d;
    break;
    case(19):  // 
    enableyzAxis = !enableyzAxis;
    break;
    case(20):  // 
    enableAnimation = !enableAnimation;
    break;
    case(21):  // 
    numLeaves = (int)(theEvent.controller().value());
    break;
    
  }
  setAmbient();
}

public void mouseDragged() 
{
  int xDiff = mouseX - pmouseX;
  int yDiff = mouseY - pmouseY;
  
  if (abs(xDiff)>abs(yDiff))
  {
    if (xDiff>0)
      rotationX += 0.1f;
    else 
      rotationX -=0.1f;
  }
  else
  {
    if (yDiff>0)
      rotationZ += 0.1f;
    else 
      rotationZ -=0.1f;
  } 
}

public void mousePressed() {
  // mouseEvent variable contains the current event information
  if (mouseEvent.getClickCount()>=2) 
  {
    //println("<double click>");
    if (mouseEvent.getButton()==MouseEvent.BUTTON1)
      scaleTree*=1.1f;
    else 
      scaleTree/=1.1f;
  }
  //if (mouseEvent.getButton()==MouseEvent.BUTTON3) println("<right button>");
  // this prints out the event in descriptive form
  println(mouseEvent);
}

  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#F0F0F0", "albero" });
  }
}
